home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2006 May / PCWMAY06.iso / Software / Toolkit / Songbird 0.1 / Songbird_0_1_0.exe / components / sbIPlaylist.js < prev    next >
Encoding:
Text File  |  2006-02-07  |  17.6 KB  |  654 lines

  1. /*
  2.  //
  3. // BEGIN SONGBIRD GPL
  4. // 
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright⌐ 2006 Pioneers of the Inevitable LLC
  8. // http://songbirdnest.com
  9. // 
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the ôGPLö).
  12. // 
  13. // Software distributed under the License is distributed 
  14. // on an ôAS ISö basis, WITHOUT WARRANTY OF ANY KIND, either 
  15. // express or implied. See the GPL for the specific language 
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this 
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc., 
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. // 
  23. // END SONGBIRD GPL
  24. //
  25.  */
  26.  
  27. //
  28. // sbIPlaylist Object
  29. //
  30.  
  31.  
  32. const SONGBIRD_PLAYLIST_CONTRACTID = "@songbird.org/Songbird/Playlist;1";
  33. const SONGBIRD_PLAYLIST_CLASSNAME = "Songbird Playlist Interface"
  34. const SONGBIRD_PLAYLIST_CID = Components.ID('{C2B560D7-A145-4dd3-9040-F1682F17BCA6}');
  35. const SONGBIRD_PLAYLIST_IID = Components.interfaces.sbIPlaylist;
  36.  
  37. const PLAYLIST_LIST_TABLE_NAME = "playlist_list";
  38.  
  39. function CPlaylist()
  40. {
  41.   var query = Components.classes["@songbird.org/Songbird/DatabaseQuery;1"].createInstance();
  42.   query = query.QueryInterface(Components.interfaces.sbIDatabaseQuery);
  43.  
  44.   this.m_internalQueryObject = query;
  45. }
  46.  
  47. CPlaylist.prototype.constructor = CPlaylist;
  48.  
  49. /* the CPlaylist class def */
  50. CPlaylist.prototype = 
  51. {
  52.   m_strName: "",
  53.   m_strReadableName: "",
  54.   
  55.   m_queryObject: null,
  56.   m_internalQueryObject: null,
  57.   
  58.   SetQueryObject: function(queryObj)
  59.   {
  60.     this.m_queryObject = queryObj; 
  61.     this.m_internalQueryObject.SetDatabaseGUID(queryObj.GetDatabaseGUID());
  62.     
  63.     return;
  64.   },
  65.   
  66.   GetQueryObject: function()
  67.   {
  68.     return this.m_queryObject;
  69.   },
  70.   
  71.   AddByGUID: function(mediaGUID, serviceGUID, nPosition, bReplace, bWillRunLater)
  72.   {
  73.     if(this.m_queryObject != null)
  74.     {
  75.       if(!bWillRunLater)
  76.         this.m_queryObject.ResetQuery();
  77.       
  78.       if(bReplace)
  79.       {
  80.         var index = this.FindByGUID(mediaGUID);
  81.         if(index != -1)
  82.           return true;
  83.       }
  84.  
  85.       this.m_queryObject.AddQuery("INSERT INTO \"" + this.m_strName + "\" (playlist_uuid, playlist_service_uuid) VALUES (\"" + mediaGUID + "\", \"" + serviceGUID + "\")");
  86.       
  87.       if(!bWillRunLater)
  88.       {
  89.         this.m_queryObject.Execute();
  90.         this.m_queryObject.WaitForCompletion();
  91.       }
  92.       
  93.       return true;
  94.     }
  95.     
  96.     return false;
  97.   },
  98.   
  99.   RemoveByGUID: function(mediaGUID, bWillRunLater)
  100.   {
  101.     if(this.m_queryObject != null)
  102.     {
  103.       if(!bWillRunLater)
  104.         this.m_queryObject.ResetQuery();
  105.  
  106.       this.m_queryObject.AddQuery("DELETE FROM \"" + this.m_strName + "\" WHERE playlist_uuid = \"" + mediaGUID + "\"");
  107.       
  108.       if(!bWillRunLater)
  109.       {
  110.         this.m_queryObject.Execute();
  111.         this.m_queryObject.WaitForCompletion();
  112.       }
  113.       
  114.       return true;
  115.     }
  116.     
  117.     return false;
  118.   },
  119.   
  120.   RemoveByIndex: function(mediaIndex, bWillRunLater)
  121.   {
  122.     if(this.m_queryObject != null)
  123.     {
  124.       if(!bWillRunLater)
  125.         this.m_queryObject.ResetQuery();
  126.  
  127.       this.m_queryObject.AddQuery("DELETE FROM \"" + this.m_strName + "\" WHERE playlist_id = \"" + mediaIndex + "\"");
  128.       
  129.       if(!bWillRunLater)
  130.       {
  131.         this.m_queryObject.Execute();
  132.         this.m_queryObject.WaitForCompletion();
  133.       }
  134.       
  135.       return true;
  136.     }
  137.     
  138.     return false;
  139.   },
  140.   
  141.   MoveByGUID: function(mediaGUID, nPosition)
  142.   {
  143.     if(m_queryObject != null)
  144.     {
  145.       
  146.       return true;
  147.     }
  148.  
  149.     return false;
  150.   },
  151.   
  152.   MoveByIndex: function(mediaIndex, nPosition)
  153.   {
  154.     if(m_queryObject != null)
  155.     {
  156.       
  157.       return true;
  158.     }
  159.  
  160.     return false;
  161.   },
  162.  
  163.   FindByGUID: function(mediaGUID)
  164.   {
  165.     if(this.m_internalQueryObject != null)
  166.     {
  167.       this.m_internalQueryObject.ResetQuery();
  168.       this.m_internalQueryObject.AddQuery("SELECT playlist_id FROM \"" + this.m_strName + "\" WHERE playlist_uuid = \"" + mediaGUID + "\"");
  169.       
  170.       this.m_internalQueryObject.Execute();
  171.       this.m_internalQueryObject.WaitForCompletion();
  172.       
  173.       var resObj = this.m_internalQueryObject.GetResultObject();
  174.       return resObj.GetRowCell(0, 0);
  175.     }
  176.     
  177.     return -1;
  178.   },
  179.   
  180.   FindByIndex: function(mediaIndex)
  181.   {
  182.     if(this.m_internalQueryObject != null)
  183.     {
  184.       this.m_internalQueryObject.ResetQuery();
  185.       this.m_internalQueryObject.AddQuery("SELECT playlist_uuid FROM \"" + this.m_strName + "\" WHERE playlist_id = \"" + nIndex + "\"");
  186.       
  187.       this.m_internalQueryObject.Execute();
  188.       this.m_internalQueryObject.WaitForCompletion();
  189.       
  190.       var resObj = this.m_internalQueryObject.GetResultObject();
  191.       return resObj.GetRowCell(0, 0);
  192.     }
  193.     
  194.     return "";
  195.   },
  196.  
  197.   GetColumnInfo: function()
  198.   {
  199.     if(this.m_queryObject != null)
  200.     {
  201.       this.m_queryObject.ResetQuery();
  202.       this.m_queryObject.AddQuery("SELECT * FROM \"" + this.m_strName + "_desc\" UNION SELECT * FROM library_desc ORDER BY sort_weight, column_name ASC");
  203.       
  204.       this.m_queryObject.Execute();
  205.       this.m_queryObject.WaitForCompletion();
  206.     }
  207.   },
  208.   
  209.   SetColumnInfo: function(strColumn, strReadableName, isVisible, defaultVisibility, isMetadata, sortWeight, colWidth, bWillRunLater)
  210.   {
  211.     if(this.m_queryObject != null)
  212.     {
  213.       if(!bWillRunLater)
  214.         this.m_queryObject.ResetQuery();
  215.       
  216.       var strQuery = "UPDATE \"" + this.m_strName + "_desc\" SET ";
  217.       strQuery += "readable_name = \"" + strReadableName + "\", ";
  218.       strQuery += "is_visible = \"" + isVisible ? 1: 0 + "\", ";
  219.       strQuery += "default_visibility = \"" + defaultVisibility ? 1 : 0 + "\", ";
  220.       strQuery += "is_metadata = \"" + isMetadata ? 1 : 0 + "\", ";
  221.       strQuery += "sort_weight = \"" + sortWeight + "\", ";
  222.       strQuery += "width = \"" + colWidth + "\" ";
  223.       strQuery += "WHERE = \"" + strColumn + "\"";
  224.       
  225.       this.m_queryObject.AddQuery(strQuery);
  226.       
  227.       strQuery = "UPDATE \"library_desc\" SET ";
  228.       strQuery += "readable_name = \"" + strReadableName + "\", ";
  229.       strQuery += "is_visible = \"" + isVisible ? 1: 0 + "\", ";
  230.       strQuery += "default_visibility = \"" + defaultVisibility ? 1 : 0 + "\", ";
  231.       strQuery += "is_metadata = \"" + isMetadata ? 1 : 0 + "\", ";
  232.       strQuery += "sort_weight = \"" + sortWeight + "\", ";
  233.       strQuery += "width = \"" + colWidth + "\" ";
  234.       strQuery += "WHERE = \"" + strColumn + "\"";
  235.       
  236.       this.m_queryObject.AddQuery(strQuery);
  237.       
  238.       if(!bWillRunLater)
  239.       {
  240.         this.m_queryObject.Execute();
  241.         this.m_queryObject.WaitForCompletion();
  242.       }
  243.     }    
  244.   },
  245.  
  246.   GetTableInfo: function()
  247.   {
  248.     if(this.m_queryObject != null)
  249.     {
  250.       this.m_queryObject.ResetQuery();
  251.       this.m_queryObject.AddQuery("PRAGMA table_info(\"" + this.m_strName + "\")");
  252.       
  253.       this.m_queryObject.Execute();
  254.       this.m_queryObject.WaitForCompletion();
  255.     }
  256.   },
  257.   
  258.   AddColumn: function(strColumn, strDataType)
  259.   {
  260.     if(this.m_queryObject != null)
  261.     {
  262.       this.m_queryObject.ResetQuery();
  263.       this.m_queryObject.AddQuery("ALTER TABLE \"" + this.m_strName + "\" ADD COLUMN \"" + strColumn + "\" " + strDataType);
  264.       this.m_queryObject.AddQuery("INSERT OR REPLACE INTO \"" + this.m_strName + "_desc\" (column_name) VALUES (\"" + strColumn + "\")");
  265.       
  266.       this.m_queryObject.Execute();
  267.       this.m_queryObject.WaitForCompletion();
  268.     }
  269.  
  270.     return;
  271.   },
  272.   
  273.   DeleteColumn: function(strColumn)
  274.   {
  275.     return;
  276.   },
  277.  
  278.   GetNumEntries: function()
  279.   {
  280.     if(this.m_queryObject != null)
  281.     {
  282.       this.m_queryObject.ResetQuery();
  283.       this.m_queryObject.AddQuery("SELECT COUNT(playlist_id) FROM \"" + this.m_strName + "\"");
  284.       
  285.       this.m_queryObject.Execute();
  286.       this.m_queryObject.WaitForCompletion();
  287.       
  288.       var resObj = this.m_queryObject.GetResultObject();
  289.       
  290.       return resObj.GetRowCell(0, 0);
  291.     }
  292.     
  293.     return 0;
  294.   },  
  295.   
  296.   GetEntry: function(nEntry)
  297.   {
  298.     if(this.m_queryObject != null)
  299.     {
  300.       this.m_queryObject.ResetQuery();
  301.       this.m_queryObject.AddQuery("SELECT * FROM \"" + this.m_strName + "\" WHERE playlist_id = \"" + nEntry + "\"");
  302.       
  303.       this.m_queryObject.Execute();
  304.       this.m_queryObject.WaitForCompletion();
  305.  
  306.       return 1;
  307.     }
  308.     
  309.     return 0;
  310.   },
  311.  
  312.   GetAllEntries: function()
  313.   {
  314.     if(this.m_queryObject != null)
  315.     {
  316.       this.m_queryObject.ResetQuery();
  317.       this.m_queryObject.AddQuery("SELECT * FROM \"" + this.m_strName + "\"");
  318.       
  319.       this.m_queryObject.Execute();
  320.       this.m_queryObject.WaitForCompletion();
  321.       
  322.       var resObj = this.m_queryObject.GetResultObject();
  323.       return resObj.GetRowCount();
  324.     }
  325.     
  326.     return 0;
  327.   },
  328.  
  329.   GetColumnValueByIndex: function(mediaIndex, strColumn)
  330.   {
  331.     if(this.m_queryObject != null)
  332.     {
  333.       this.m_queryObject.ResetQuery();
  334.       this.m_queryObject.AddQuery("SELECT " + strColumn + " FROM \"" + this.m_strName + "\" WHERE playlist_id = \"" + mediaIndex + "\"");
  335.       
  336.       this.m_queryObject.Execute();
  337.       this.m_queryObject.WaitForCompletion();
  338.       
  339.       var resObj = this.m_queryObject.GetResultObject();
  340.  
  341.       if(resObj.GetRowCount())
  342.         return resObj.GetRowCell(0, 0);
  343.     }
  344.         
  345.     return "";
  346.   },
  347.   
  348.   GetColumnValueByGUID: function(mediaGUID, strColumn)
  349.   {
  350.     if(this.m_queryObject != null)
  351.     {
  352.       this.m_queryObject.ResetQuery();
  353.       this.m_queryObject.AddQuery("SELECT " + strColumn + " FROM \"" + this.m_strName + "\" WHERE playlist_uuid = \"" + mediaGUID + "\"");
  354.       
  355.       this.m_queryObject.Execute();
  356.       this.m_queryObject.WaitForCompletion();
  357.       
  358.       var resObj = this.m_queryObject.GetResultObject();
  359.  
  360.       if(resObj.GetRowCount())
  361.         return resObj.GetRowCell(0, 0);    
  362.     }
  363.       
  364.     return "";
  365.   },
  366.  
  367.   GetColumnValuesByIndex: function(mediaIndex, nColumnCount, aColumns, nValueCount)
  368.   {
  369.     nValueCount = 0;
  370.     var aValues = new Array();
  371.  
  372.     if(this.m_queryObject != null)
  373.     {
  374.       var strQuery = "SELECT ";
  375.       this.m_queryObject.ResetQuery();
  376.       
  377.       var i = 0;
  378.       for( ; i < nColumnCount; i++)
  379.       {
  380.         strQuery += aColumns[i];
  381.         
  382.         if(i < nColumnCount - 1)
  383.           strQuery += ", ";
  384.       }
  385.       
  386.       strQuery += " FROM \"" + this.m_strName + "\" WHERE playlist_id = \"" + mediaIndex + "\"";
  387.       this.m_queryObject.AddQuery(strQuery);
  388.       
  389.       this.m_queryObject.Execute();
  390.       this.m_queryObject.WaitForCompletion();
  391.       
  392.       var resObj = this.m_queryObject.GetResultObject();
  393.       nValueCount = resObj.GetColumnCount();
  394.       
  395.       for(var i = 0; i < nValueCount; i++)
  396.       {
  397.         aValues.push(resObj.GetRowCell(0, i));
  398.       }    
  399.     }
  400.     
  401.     return aValues;
  402.   },
  403.   
  404.   GetColumnValuesByGUID: function(mediaGUID, nColumnCount, aColumns, nValueCount)
  405.   {
  406.     nValueCount = 0;
  407.     var aValues = new Array();
  408.  
  409.     if(this.m_queryObject != null)
  410.     {
  411.       var strQuery = "SELECT ";
  412.       this.m_queryObject.ResetQuery();
  413.       
  414.       var i = 0;
  415.       for( ; i < nColumnCount; i++)
  416.       {
  417.         strQuery += aColumns[i];
  418.         
  419.         if(i < nColumnCount - 1)
  420.           strQuery += ", ";
  421.       }
  422.       
  423.       strQuery += " FROM \"" + this.m_strName + "\" WHERE playlist_uuid = \"" + mediaGUID + "\"";
  424.       this.m_queryObject.AddQuery(strQuery);
  425.       
  426.       this.m_queryObject.Execute();
  427.       this.m_queryObject.WaitForCompletion();
  428.       
  429.       var resObj = this.m_queryObject.GetResultObject();
  430.       nValueCount = resObj.GetColumnCount();
  431.       
  432.       for(var i = 0; i < nValueCount; i++)
  433.       {
  434.         aValues.push(resObj.GetRowCell(0, i));
  435.       }    
  436.     }
  437.     
  438.     return aValues;
  439.   },
  440.   
  441.   SetColumnValueByIndex: function(mediaIndex, strColumn, strValue)
  442.   {
  443.     if(this.m_queryObject != null)
  444.     {
  445.       if(!bWillRunLater)
  446.         this.m_queryObject.ResetQuery();
  447.       
  448.       strValue = strValue.replace(/"/g, "\"\"");
  449.       this.m_queryObject.AddQuery("UPDATE \"" + this.m_strName + "\" SET " + strColumn + " = \"" + strValue + "\" WHERE playlist_id = \"" + mediaIndex + "\"");
  450.       
  451.       if(!bWillRunLater)
  452.       {
  453.         this.m_queryObject.Execute();
  454.         this.m_queryObject.WaitForCompletion();
  455.       }
  456.     }
  457.     
  458.     return;
  459.   },
  460.   
  461.   SetColumnValueByGUID: function(mediaGUID, strColumn, strValue)
  462.   {
  463.     if(this.m_queryObject != null)
  464.     {
  465.       if(!bWillRunLater)
  466.         this.m_queryObject.ResetQuery();
  467.       
  468.       strValue = strValue.replace(/"/g, "\"\"");
  469.       this.m_queryObject.AddQuery("UPDATE \"" + this.m_strName + "\" SET " + strColumn + " = \"" + strValue + "\" WHERE playlist_uuid = \"" + mediaGUID + "\"");
  470.       
  471.       if(!bWillRunLater)
  472.       {
  473.         this.m_queryObject.Execute();
  474.         this.m_queryObject.WaitForCompletion();
  475.       }
  476.     }
  477.     
  478.     return;
  479.   },
  480.  
  481.   SetColumnValuesByIndex: function(mediaIndex, nColumnCount, aColumns, nValueCount, aValues)
  482.   {
  483.     if(this.m_queryObject != null ||
  484.        nColumnCount != nValueCount)
  485.     {
  486.       if(!bWillRunLater)
  487.         this.m_queryObject.ResetQuery();
  488.       
  489.       var strQuery = "UPDATE \"" + this.m_strName + "\" SET ";
  490.       var i = 0;
  491.       for(; i < nColumnCount; i++)
  492.       {
  493.         aValues[i] = aValues[i].replace(/"/g, "\"\"");
  494.         strQuery += aColumns[i] + " = \"" + aValues[i] + "\"";
  495.         if(i < nColumnCount - 1)
  496.           strQuery += ", ";
  497.       }
  498.       
  499.       strQuery += " WHERE id = \"" + mediaIndex + "\"";
  500.       this.m_queryObject.AddQuery(strQuery);
  501.       
  502.       if(!bWillRunLater)
  503.       {
  504.         this.m_queryObject.Execute();
  505.         this.m_queryObject.WaitForCompletion();
  506.       }
  507.     }
  508.  
  509.     return;
  510.   },
  511.   
  512.   SetColumnValuesByGUID: function(mediaGUID, nColumnCount, aColumns, nValueCount, aValues)
  513.   {
  514.     if(this.m_queryObject != null ||
  515.        nColumnCount != nValueCount)
  516.     {
  517.       if(!bWillRunLater)
  518.         this.m_queryObject.ResetQuery();
  519.       
  520.       var strQuery = "UPDATE \"" + this.m_strName + "\" SET ";
  521.       var i = 0;
  522.       for(; i < nColumnCount; i++)
  523.       {
  524.         aValues[i] = aValues[i].replace(/"/g, "\"\"");
  525.         strQuery += aColumns[i] + " = \"" + aValues[i] + "\"";
  526.         if(i < nColumnCount - 1)
  527.           strQuery += ", ";
  528.       }
  529.       
  530.       strQuery += " WHERE playlist_uuid = \"" + mediaGUID + "\"";
  531.       this.m_queryObject.AddQuery(strQuery);
  532.       
  533.       if(!bWillRunLater)
  534.       {
  535.         this.m_queryObject.Execute();
  536.         this.m_queryObject.WaitForCompletion();
  537.       }
  538.     }
  539.  
  540.     return;
  541.   },
  542.   
  543.   SetName: function(strName)
  544.   {
  545.     this.m_strName = strName;
  546.     return;
  547.   },
  548.   
  549.   GetName: function()
  550.   {
  551.     return this.m_strName;
  552.   },
  553.   
  554.   SetReadableName: function(strReadableName)
  555.   {
  556.     this.m_queryObject.ResetQuery();
  557.     
  558.     strReadableName = strReadableName.replace(/"/g, "\"\"");
  559.     this.m_queryObject.AddQuery("UPDATE " + PLAYLIST_LIST_TABLE_NAME + " SET readable_name = \"" + strReadableName + "\" WHERE name = \"" + this.m_strName + "\"");
  560.     
  561.     this.m_queryObject.Execute();
  562.     this.m_queryObject.WaitForCompletion();
  563.     
  564.     return;
  565.   },
  566.   
  567.   GetReadableName: function()
  568.   {
  569.     var strReadableName = "";
  570.     
  571.     this.m_queryObject.ResetQuery();
  572.     this.m_queryObject.AddQuery("SELECT readable_name FROM " + PLAYLIST_LIST_TABLE_NAME + " WHERE name = \"" + this.m_strName + "\"");
  573.     
  574.     this.m_queryObject.Execute();
  575.     this.m_queryObject.WaitForCompletion();
  576.     
  577.     var resObj = this.m_queryObject.GetResultObject();
  578.     
  579.     if(resObj.GetRowCount())
  580.       strReadableName = resObj.GetRowCell(0, 0);    
  581.     
  582.     return strReadableName;
  583.   },
  584.  
  585.   QueryInterface: function(iid)
  586.   {
  587.       if (!iid.equals(Components.interfaces.nsISupports) &&
  588.           !iid.equals(SONGBIRD_PLAYLIST_IID))
  589.           throw Components.results.NS_ERROR_NO_INTERFACE;
  590.       return this;
  591.   }
  592. };
  593.  
  594. /**
  595.  * \class sbPlaylistModule
  596.  * \brief 
  597.  */
  598. var sbPlaylistModule = 
  599. {
  600.   registerSelf: function(compMgr, fileSpec, location, type)
  601.   {
  602.       compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  603.       compMgr.registerFactoryLocation(SONGBIRD_PLAYLIST_CID, 
  604.                                       SONGBIRD_PLAYLIST_CLASSNAME, 
  605.                                       SONGBIRD_PLAYLIST_CONTRACTID, 
  606.                                       fileSpec, 
  607.                                       location,
  608.                                       type);
  609.   },
  610.  
  611.   getClassObject: function(compMgr, cid, iid) 
  612.   {
  613.       if (!cid.equals(SONGBIRD_PLAYLIST_CID))
  614.           throw Components.results.NS_ERROR_NO_INTERFACE;
  615.  
  616.       if (!iid.equals(Components.interfaces.nsIFactory))
  617.           throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  618.  
  619.       return sbPlaylistFactory;
  620.   },
  621.  
  622.   canUnload: function(compMgr)
  623.   { 
  624.     return true; 
  625.   }
  626. };
  627.  
  628. /**
  629.  * \class sbPlaylistFactory
  630.  * \brief 
  631.  */
  632. var sbPlaylistFactory =
  633. {
  634.     createInstance: function(outer, iid)
  635.     {
  636.         if (outer != null)
  637.             throw Components.results.NS_ERROR_NO_AGGREGATION;
  638.     
  639.         if (!iid.equals(SONGBIRD_PLAYLIST_IID) &&
  640.             !iid.equals(Components.interfaces.nsISupports))
  641.             throw Components.results.NS_ERROR_INVALID_ARG;
  642.  
  643.         return (new CPlaylist()).QueryInterface(iid);
  644.     }
  645. }; //sbPlaylistFactory
  646.  
  647. /**
  648.  * \function NSGetModule
  649.  * \brief 
  650.  */
  651. function NSGetModule(comMgr, fileSpec)
  652.   return sbPlaylistModule;
  653. } //NSGetModule